﻿using NovoDataBase;
using NovoSerDes.Funcs;
using Sunny.UI;
using Sunny.UI.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;

namespace NovoSerDes
{
    public partial class Form_RegDump : UIPage
    {
        SerDesComm commDev;
        RegisterMap regmap = null;
        RegisterMap defaultRegMap = null;
        public Form_RegDump(SerDesComm _commDev, RegisterMap _regmap, RegisterMap _defaultRegMap, byte devAddr)
        {
            InitializeComponent();
            this.commDev = _commDev;
            this.cmb_IICMode.SelectedIndex = 3;

            this.regmap = _regmap;
            this.defaultRegMap = _defaultRegMap;
            this.numUD_ChipAddr.Value = devAddr;
        }
        private AddressMeditor meditor;
        public AddressMeditor Meditor
        {
            get { return meditor; }
            set
            {
                meditor = value;
                meditor.AddressChanged += Meditor_AddressChanged;
            }
        }
        private void Meditor_AddressChanged(object sender, string newAddrss)
        {
            byte DevAddr = Convert.ToByte(newAddrss.Replace("0x", ""), 16);
            this.numUD_ChipAddr.Value = DevAddr;
        }

        private USBDeviceMeditor usbDeveiceMetor;
        public USBDeviceMeditor UsbDeviceMetor
        {
            get { return usbDeveiceMetor; }
            set
            {
                usbDeveiceMetor = value;
                usbDeveiceMetor.USBDeviceChanged += usbDevice_DeviceChange;
            }
        }

        private void usbDevice_DeviceChange(object sender, SerDesComm newSerdesComm)
        {
            this.commDev = newSerdesComm;
        }
        private IICMode IICModeDecode(string msg)
        {
            IICMode i2cMode = IICMode.A16D32;
            if (Enum.TryParse(msg.ToUpper(), true, out i2cMode))
                return i2cMode;
            else
                return IICMode.None;
        }

        private List<string[]> ReadRegister(byte devAddr, UInt16 startRegAddr, UInt16 readCount, bool ifIncrease, IICMode i2cMode)
        {
            if(commDev == null) 
                return null;

            List<string[]> regList = new List<string[]> { };

            object regData = 0;
            UInt16 tempRegAddr = 0;
            int byteCnt_RegAddr = ((byte)i2cMode >> 4) & 0x0F;
            int byteCnt_RegData = ((byte)i2cMode & 0x0F);
            for (uint ix = 0; ix < readCount; ix++) 
            {
                if(ifIncrease)
                    tempRegAddr = (UInt16)(startRegAddr + (ix * byteCnt_RegData));
                else
                    tempRegAddr = (UInt16)(startRegAddr - (ix * byteCnt_RegData));

                if (!commDev.IICRead(devAddr, tempRegAddr, out regData, i2cMode))
                {
                    MessageBox.Show(String.Format("Read Reg:0x{0} failed!", ix.ToString("X4")));
                    break;
                }
                regList.Add(new string[] { 
                    "0x" + (tempRegAddr).ToString(String.Format("X{0}", 2 * byteCnt_RegAddr)),  
                    "0x" + Convert.ToUInt32(regData).ToString(String.Format("X{0}", 2 * byteCnt_RegData))
                    });

                this.lbl_Progress.Text = ((double)(ix+1)/(double)(readCount) *100).ToString("F1") + "%";
                this.Refresh();
            }            

            return regList;
        }


        private string[] CombineRegInfo(Register defaultReg, Register reg, UInt32 readData)
        {
            if (reg == null || (defaultReg.RegAddress != reg.RegAddress))
                return null;

            string[] allLine = new string[reg.BFCount];
            allLine[0] = string.Join(",", new string[]
            {
                reg.RegName,
                //reg.RegDescription,
                "0x" + reg.RegAddress.ToString("X4"),
                reg[0].RWPro.ToString(),
                reg[0].BFName,
                reg[0].BITs,
                //reg[0].BFDesc,
                "0x" + defaultReg[0].BFValueString
            }); ;
            for (int ix_bf = 1; ix_bf < reg.BFCount; ix_bf++)
            {
                allLine[ix_bf] = string.Join(",", new string[]
                {
                    "",
                    //"",
                    "",
                    reg[ix_bf].RWPro.ToString(),
                    reg[ix_bf].BFName,
                    reg[ix_bf].BITs,
                    //reg[ix_bf].BFDesc,
                    "0x" + defaultReg[ix_bf].BFValueString
                });
            }

            reg.RegValue = (UInt32)readData;
            string mark = string.Empty;
            if (reg[0].BFValue != defaultReg[0].BFValue)
                mark = "1";
            else
                mark = "0";
            allLine[0] = string.Join(",",  allLine[0], reg[0].BFValueString, mark);

            for (int ix_bf = 1; ix_bf < reg.BFCount; ix_bf++)
            {
                if (reg[ix_bf].BFValue != defaultReg[ix_bf].BFValue)
                    mark = "1";
                else
                    mark = "0";

                allLine[ix_bf] = string.Join(",", allLine[ix_bf], reg[ix_bf].BFValueString, mark);
            }
            
            return allLine;
        }


        private void DumpRegMap(string path, byte devAddr, IICMode i2cMode)
        {
            if (regmap == null)
            {
                MessageBox.Show("No Regmap, can Not dump detailed register map, please use simple dump.");
                return;
            }

            // write file
            StreamWriter sw = File.CreateText(path);
            //sw.WriteLine(string.Join(",", new string[] { "Reg Name", "Reg Description", "Reg Addr", "BF Name", "Bit Description", "Default Value", "ReadBack Value" }));

            sw.WriteLine(string.Join(",", new string[] { "Reg Name", "Reg Addr", "RW", "BF Name", "BF", "Default Value", "ReadBack Value", "Diff" }));

            RegGroup tempGroup;
            RegGroup defaultGroup;
            Register tempRegister;
            Register defaultRegister;
            string[] msg;
            object regData;

            for (int ix_group = 0; ix_group < regmap.GroupCount; ix_group++)
            {
                //this.lbl_Progress.Text = (ix_group + 1).ToString() + "/" + regmap.GroupCount.ToString();                
                tempGroup = regmap.GetRegGroup(ix_group);
                //if (tempGroup.Name == "TestBus")
                //    continue;

                defaultGroup = defaultRegMap.GetRegGroup(ix_group);
                for (int ix_reg = 0; ix_reg < tempGroup.RegList.Count; ix_reg++)
                {

                    tempRegister = tempGroup.RegList[ix_reg];
                    defaultRegister = defaultGroup.RegList[ix_reg];

                    if (rdb_regMapDump.Checked && tempGroup.Name != "TestBus")
                    {
                        if (!commDev.IICRead(devAddr, tempRegister.RegAddress, out regData, i2cMode))
                        {
                            MessageBox.Show(String.Format("Read Reg:0x{0} failed!", tempRegister.RegAddress.ToString("X4")));
                            sw.Close();
                            return;
                        }
                    }
                    else if (rdb_DumpTestBus.Checked && tempGroup.Name == "TestBus")
                    {
                        byte tempTBData = 0;
                        if (!commDev.TestBusRead(devAddr, (ushort)tempRegister.RegAddress, out tempTBData))
                        {
                            MessageBox.Show(String.Format("Read Reg:0x{0} failed!", tempRegister.RegAddress.ToString("X4")));
                            sw.Close();
                            return;
                        }
                        regData = tempTBData;
                    }
                    else
                    {
                        continue;
                    }    

                    msg = CombineRegInfo(defaultRegister, tempRegister, Convert.ToUInt32(regData));
                    // Save Reg info
                    for (int ix_bf = 0; ix_bf < tempRegister.BFCount; ix_bf++)
                    {
                        sw.WriteLine(msg[ix_bf]);
                    }
                }
                this.lbl_Progress.Text = (ix_group + 1).ToString() + "/" + regmap.GroupCount.ToString();
                this.Refresh();
            }

            sw.Close();

            object regData0x2000 = 0;
            object regData0x2004 = 0;
            if (!commDev.IICRead(devAddr, 0x2000, out regData0x2000, i2cMode))
            {
                return;
            }
            if (!commDev.IICRead(devAddr, 0x2000, out regData0x2004, i2cMode))
            {
                return;
            }

            if (((UInt32)regData0x2000 != 0x0F) && ((UInt32)regData0x2000 != 0x01))
                MessageBox.Show("Some Register May Wrong!");

        }


        private void btn_SaveTo_Click(object sender, EventArgs e)
        {
            StreamWriter sw = null;
            try
            {
                this.Cursor = Cursors.WaitCursor;

                SaveFileDialog savefiledlg = new SaveFileDialog();
                savefiledlg.Title = "Dump to...";
                savefiledlg.Filter = "CSV file(*.csv)|*.csv|Text file(*.txt)|*.txt";
                savefiledlg.RestoreDirectory = true;
                string filename = "";
                if (savefiledlg.ShowDialog() == DialogResult.OK)
                {
                    filename = savefiledlg.FileName;
                }
                else
                    return;

                byte devAddr = (byte)(this.numUD_ChipAddr.Value * 2);
                UInt16 startRegAddr = (UInt16)(this.numUD_StartRegAddr.Value);
                UInt16 endRegAddr = (UInt16)(this.numUP_EndRegAddr.Value);

                UInt16 readCount = 0;// (UInt16)(this.numUD_RegCount.Value);
                bool ifIncrease = (startRegAddr <= endRegAddr) ? true : false;
                if (startRegAddr <= endRegAddr)
                    readCount = Convert.ToUInt16((endRegAddr - startRegAddr) / 4 + 1);
                else
                    readCount = Convert.ToUInt16((startRegAddr - endRegAddr) / 4 + 1);

                IICMode i2cMode = IICModeDecode(this.cmb_IICMode.SelectedItem.ToString());

                // Read RegData
                List<string[]> result = ReadRegister(devAddr, startRegAddr, readCount, ifIncrease, i2cMode);

                // write file
                sw = File.CreateText(filename);

                bool ifCSV = Path.GetExtension(filename).Contains("csv");

                // write header
                if (ifCSV)
                    sw.WriteLine(string.Join(",", new string[]{"RegAddr(Hex)", "RegData(Hex)"}));
                else
                    sw.WriteLine(string.Join(" = ", new string[] { "RegAddr(Hex)", "RegData(Hex)" }));

                for (int ix = 0; ix < result.Count; ix++)
                {
                    if(ifCSV)
                        sw.WriteLine(string.Join(",", result[ix]));
                    else
                        sw.WriteLine(string.Join(" = ", result[ix]));
                }                
            }
            catch (Exception ex)
            {
                MessageBox.Show("Save script file failed! Error:{0}", ex.Message);
            }
            finally
            {
                if(sw != null)
                    sw.Close();
                this.Cursor = Cursors.Default;
            }
        }

        private void numUD_StartRegAddr_ValueChanged(object sender, EventArgs e)
        {
            IICMode i2cMode = IICModeDecode(this.cmb_IICMode.SelectedItem.ToString());
            int byteCnt_RegData = ((byte)i2cMode & 0x0F);

            this.numUD_RegCount.Maximum = (UInt16.MaxValue + 1 - this.numUD_StartRegAddr.Value)/ byteCnt_RegData;
        }

        private void Form_RegDump_Load(object sender, EventArgs e)
        {

        }

        private void btn_DumpAll_Click(object sender, EventArgs e)
        {
            StreamWriter sw = null;
            try
            {
                this.Cursor = Cursors.WaitCursor;

                SaveFileDialog savefiledlg = new SaveFileDialog();
                savefiledlg.Title = "Dump to...";
                savefiledlg.Filter = "CSV file(*.csv)|*.csv";
                savefiledlg.RestoreDirectory = true;
                string filename = "";
                if (savefiledlg.ShowDialog() == DialogResult.OK)
                {
                    filename = savefiledlg.FileName;
                }
                else
                    return;

                byte devAddr = (byte)(this.numUD_ChipAddr.Value * 2);
                IICMode i2cMode = IICModeDecode(this.cmb_IICMode.SelectedItem.ToString());

                // Dump RegData                
                 DumpRegMap(filename, devAddr, i2cMode);
                
            }
            catch (Exception ex)
            {
                MessageBox.Show("Save script file failed! Error:{0}", ex.Message);
            }
            finally
            {
                if (sw != null)
                    sw.Close();
                this.Cursor = Cursors.Default;
            }
        }

        private void rdb_regMapDump_CheckedChanged(object sender, EventArgs e)
        {
            Point loc = new Point(80, 200);
            if (rdb_regMapDump.Checked || rdb_DumpTestBus.Checked)
            {
                this.grb_dumpRegMap.Visible = true;
                this.grb_dumpRegMap.Location = loc;
                this.grb_freeDump.Visible = false;
            }
            else
            {
                this.grb_dumpRegMap.Visible = false;
                this.grb_freeDump.Visible = true;
                this.grb_freeDump.Location = loc;
            }
        }
    }
}
